﻿using System;
using System.IO;
using System.Net;
using System.Threading;

namespace ConsoleDownloadFile
{
    internal class Program
    {
        private static readonly int BufferSize = 32768;
        private static bool _isEnd = false;

        static void Main(string[] args)
        {
            // 如果下面的连接不可用，请自行更换一个可用的下载链接
            // 如果下载太快，请更换一个大点的文件进行测试
            string url = "https://ys5.lianyitech.com.cn/mms/uploads//device-file/20231013/b28443253f924110a4c57f791dd407bc.png";

            _isEnd = downloadFile(url, "file.png");

            Console.WriteLine(_isEnd);
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="url">下载文件url</param>
        /// <param name="path">文件保存地址</param>
        /// <returns></returns>
        private static bool downloadFile(string url,string path) 
        {
            var isEnd = false;
            try
            {
                if (!WebClientDownloadInstallerFile(url, path))
                {
                    isEnd = false;
                }

                isEnd = true;
            }
            catch
            {
                isEnd = false;
            }
            return isEnd;
        }

        private static bool WebClientDownloadInstallerFile(string url, string fileName)
        {
            bool resumeDownload = IsResume(url, fileName);
            string tempFileName = fileName + ".temp";
            bool isEnd = false;
            FileMode fm = FileMode.Create;
            Stream stream = null;
            FileStream fileStream = null;
            HttpWebResponse response = null;
            try
            {
                Uri installerUrl = new Uri(url);
                HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
                httpWebRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;
                if (resumeDownload)
                {
                    FileInfo fn = new FileInfo(tempFileName);
                    httpWebRequest.AddRange(fn.Length);
                    fm = FileMode.Append;
                }

                response = (HttpWebResponse)httpWebRequest.GetResponse();
                stream = response.GetResponseStream();

                double contentLength = GetContentLength(response);
                byte[] buffer = new byte[BufferSize];
                long downloadedLength = 0;
                int currentDataLength;
                fileStream = new FileStream(tempFileName, fm);

                while ((currentDataLength = stream.Read(buffer, 0, BufferSize)) > 0)
                {
                    fileStream.Write(buffer, 0, currentDataLength);
                    downloadedLength += (long)currentDataLength;
                }

                if (currentDataLength >= 0)
                {
                    // downlown correct
                    isEnd = true;
                }
            }
            catch
            {
                // todo
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Flush();
                    fileStream.Close();
                }
                if (stream != null)
                {
                    stream.Close();
                }

                if (response != null)
                {
                    response.Close();
                }
            }
            if (isEnd)
            {
                File.Move(tempFileName, fileName);

                string tempFileInfoName = fileName + ".temp.info";
                if (File.Exists(tempFileInfoName))
                {
                    DeleteFileIfExists(tempFileInfoName);
                }
            }
            return isEnd;
        }

        private static bool IsResume(string url, string fileName)
        {
            string tempFileName = fileName + ".temp";
            string tempFileInfoName = fileName + ".temp.info";
            bool resumeDowload = false;
            HttpWebResponse response = null;

            try
            {
                HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
                request.Proxy.Credentials = CredentialCache.DefaultCredentials;
                response = (HttpWebResponse)request.GetResponse();

                if (GetAcceptRanges(response))
                {
                    string newEtag = GetEtag(response);
                    if (File.Exists(tempFileName) && File.Exists(tempFileInfoName))
                    {
                        string oldEtag = File.ReadAllText(tempFileInfoName);
                        if (!string.IsNullOrEmpty(oldEtag) && !string.IsNullOrEmpty(newEtag) && newEtag == oldEtag)
                        {
                            resumeDowload = true;
                        }
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(newEtag))
                        {
                            File.WriteAllText(tempFileInfoName, newEtag);
                        }
                    }
                }
            }
            catch
            {
                // todo
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
            return resumeDowload;
        }

        private static bool GetAcceptRanges(WebResponse res)
        {
            if (res.Headers["Accept-Ranges"] != null)
            {
                string s = res.Headers["Accept-Ranges"];
                if (s == "none")
                {
                    return false;
                }
            }
            return true;
        }

        private static string GetEtag(WebResponse res)
        {
            if (res.Headers["ETag"] != null)
            {
                return res.Headers["ETag"];
            }
            return null;
        }

        private static double GetContentLength(HttpWebResponse res)
        {
            double result = 0.0;
            if (res.Headers["Content-Length"] != null)
            {
                string s = res.Headers["Content-Length"];
                if (!double.TryParse(s, out result))
                {
                    result = 0.0;
                }
            }
            return result;
        }

        public static void DeleteFileIfExists(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException("filePath");
            }
            if (File.Exists(filePath))
            {
                try
                {
                    File.Delete(filePath);
                }
                catch (IOException)
                {
                    Thread.Sleep(500);
                    File.Delete(filePath);
                }
                catch (UnauthorizedAccessException)
                {
                    FileAttributes attributes = File.GetAttributes(filePath);
                    if ((attributes & FileAttributes.ReadOnly).Equals(FileAttributes.ReadOnly))
                    {
                        File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
                        File.Delete(filePath);
                    }
                }
            }
        }
    }
}
